package com.hup.utils.swing.customView;

import javax.swing.*;
import javax.swing.event.UndoableEditEvent;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.JTextComponent;
import javax.swing.text.PlainDocument;
import javax.swing.undo.CompoundEdit;
import javax.swing.undo.UndoManager;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;

/**
 * @author hugan
 * @date 2019/9/8
 */
public interface BaseUndoText {

    /**
     * 为文本控件添加撤回功能:
     * .实现快捷键功能
     * .解决:setText()后撤销时会先显示空白字符串的问题,
     * .解决:用户不会撤回到 程序初始化过程中改变过的文本
     */
    @SuppressWarnings("Duplicates")
    static void initUndo(JTextComponent c, UndoManager undoManager) {
        UndoDocument document = new UndoDocument();
        document.addUndoableEditListener(undoManager);
        c.setDocument(document);
        c.addFocusListener(new FocusAdapter() {
            @Override
            public void focusGained(FocusEvent e) {
                //第一次获取到焦点时,就重置undoManager(清空 程序初始化过程中改变过的文本),并移除本监听
                undoManager.discardAllEdits();
                c.removeFocusListener(this);
            }
        });

        c.registerKeyboardAction(e -> {
            if (undoManager.canUndo()) {
                int cursor = c.getSelectionStart();//恢复光标
                undoManager.undo();
                c.setSelectionStart(cursor);
                c.setSelectionEnd(cursor);
            }
        }, KeyStroke.getKeyStroke(KeyEvent.VK_Z, InputEvent.CTRL_MASK), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
        c.registerKeyboardAction(e -> {
            if (undoManager.canRedo()) {
                int cursor = c.getSelectionStart();//恢复光标
                undoManager.redo();
                c.setSelectionStart(cursor);
                c.setSelectionEnd(cursor);
            }
        }, KeyStroke.getKeyStroke(KeyEvent.VK_Y, InputEvent.CTRL_MASK), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
    }

    default void initUndo(UndoManager undoManager) {
        JTextComponent c = (JTextComponent) this;
        initUndo(c, undoManager);
    }

    UndoManager getUndoMgr();

    /**
     * 处理 使用 {@link UndoManager}时, setText()方法[先清空,然后加入字符串]导致撤销时出现空白内容
     * @apiNote swing的这个功能实现得太无语了,源码易引入性差(简单引入后,用户体验极差);
     * 要出用户实用的效果时,前期是学习成本高,开发中途是代码调用繁琐
     */
    class UndoDocument extends PlainDocument {
        private CompoundEdit compoundEdit;

        @Override
        protected void fireUndoableEditUpdate(UndoableEditEvent e) {
            if (compoundEdit == null) {
                super.fireUndoableEditUpdate(e);
            } else {
                compoundEdit.addEdit(e.getEdit());
            }
        }

        @Override
        public void replace(int offset, int length, String text, AttributeSet attrs) throws BadLocationException {
            if (length == 0) {
                super.replace(offset, length, text, attrs);
            } else {
                compoundEdit = new CompoundEdit();
                super.fireUndoableEditUpdate(new UndoableEditEvent(this, compoundEdit));
                super.replace(offset, length, text, attrs);
                compoundEdit.end();
                compoundEdit = null;
            }
        }
    }
}
